﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Runtime.Serialization;
using System.ServiceModel;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Metadata;

namespace CrmLinkBuddyWpf
{
    public class CrmMetadataRetriever
    {
        private IOrganizationService _crmService;
        private CrmMetadata _crmMetadataDS;
        private CrmMetadata.EntityDataTable _entityDT;

        #region Constructors

        public CrmMetadataRetriever(IOrganizationService crmService)
        {
            _crmService = crmService;
            _crmMetadataDS = new CrmMetadata();
            InitializeDataSetTables();
        }
        
        #endregion Constructors

        #region Public Metadata Retrieval Methods

        public void ClearStoredMetadata()
        {
            _crmMetadataDS.Clear();
        }

        public void RetrieveEntityMetadata()
        {
            var request = new RetrieveAllEntitiesRequest()
            {
                RetrieveAsIfPublished = true,
                EntityFilters = EntityFilters.Default
            };

            RetrieveAllEntitiesResponse response;
            try
            {
                response = (RetrieveAllEntitiesResponse)_crmService.Execute(request);
            }
            catch (FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault> ex)
            {
                Trace.Write(ex.ToString());
                throw;
            }
            catch (System.ServiceModel.Security.MessageSecurityException ex)
            {
                Trace.Write(ex.ToString());
                throw;
            }

            foreach (EntityMetadata entity in response.EntityMetadata)
            {
                AddEntityMetadataToDataSet(entity);
            }
        }

        #endregion

        #region Entity Methods

        public List<string> GetCustomEntityNames()
        {
            List<string> entityList = new List<string>();
            DataView dv = new DataView(_entityDT);
            dv.RowFilter = String.Format("IsCustomEntity = 'TRUE'");
            if (dv.Count > 0)
            {
                foreach (DataRowView drv in dv)
                {
                    string entityLogicalName = drv["LogicalName"].ToString();
                    if (!entityLogicalName.StartsWith("msdyn_"))
                    {
                        entityList.Add(entityLogicalName);
                    }
                }
            }
            return entityList;
        }

        public List<string> GetQueryableEntityLogicalNames()
        {
            List<string> entityLogicalNames = new List<string>();
            foreach (CrmMetadata.EntityRow entityDR in _entityDT.Rows)
            {
                if (!entityDR.IsPrimaryNameAttributeNull() && string.Compare(entityDR.IsValidForAdvancedFind, "true", ignoreCase: true) == 0)
                {
                    entityLogicalNames.Add(entityDR.LogicalName);
                }
            }
            entityLogicalNames.Sort();
            return entityLogicalNames;
        }

        /// <summary>
        /// Get the name of the attribute for an entity that is the primary id (primary key).
        /// </summary>
        /// <param name="entityLogicalName"></param>
        /// <returns></returns>
        /// <example>GetEntityPrimaryIdAttributeName("phonecall") returns "activityid"</example>
        public string GetEntityPrimaryIdAttributeName(string entityLogicalName)
        {
            string entityPrimaryIdAttributeName = string.Empty;
            DataView dv = new DataView(_entityDT);
            dv.RowFilter = String.Format("LogicalName = '{0}'", entityLogicalName);
            if (dv.Count > 0)
                entityPrimaryIdAttributeName = dv[0]["PrimaryIdAttribute"].ToString();
            return entityPrimaryIdAttributeName;
        }

        public string GetEntityPrimaryNameAttribute(string entityName)
        {
            string primaryNameAttribute = string.Empty;
            DataView dv = new DataView(_entityDT);
            dv.RowFilter = string.Format("LogicalName = '{0}' AND PrimaryNameAttribute IS NOT NULL", entityName);
            if (dv.Count == 1)
            {
                primaryNameAttribute = dv[0]["PrimaryNameAttribute"].ToString();
            }
            return primaryNameAttribute;
        }

        #endregion Entity Methods

        #region Private Methods

        private void InitializeDataSetTables()
        {
            _entityDT = _crmMetadataDS.Entity;
        }

        private void AddEntityMetadataToDataSet(EntityMetadata entityMetadata)
        {
            string displayName = string.Empty;

            CrmMetadata.EntityRow entityDR = _entityDT.NewEntityRow();
            entityDR.ActivityTypeMask = entityMetadata.ActivityTypeMask.ToString();
            entityDR.ActivityTypeMask = entityMetadata.ActivityTypeMask.ToString();
            entityDR.CanBePrimaryEntityInRelationship = entityMetadata.CanBePrimaryEntityInRelationship.Value.ToString();
            entityDR.CanBeRelatedEntityInRelationship = entityMetadata.CanBeRelatedEntityInRelationship.Value.ToString();
            entityDR.CanCreateAttributes = entityMetadata.CanCreateAttributes.Value.ToString();
            if (entityMetadata.Description.UserLocalizedLabel != null)
                entityDR.Description = entityMetadata.Description.UserLocalizedLabel.Label;

            if (entityMetadata.DisplayName.UserLocalizedLabel != null)
            {
                entityDR.DisplayName = entityMetadata.DisplayName.UserLocalizedLabel.Label;
                displayName = entityDR.DisplayName;
            }

            entityDR.IsActivity = entityMetadata.IsActivity.Value.ToString();
            entityDR.IsActivityParty = entityMetadata.IsActivityParty.Value.ToString();
            entityDR.IsConnectionsEnabled = entityMetadata.IsConnectionsEnabled.Value.ToString();
            entityDR.IsCustomEntity = entityMetadata.IsCustomEntity.Value.ToString();
            entityDR.IsCustomizable = entityMetadata.IsCustomizable.Value.ToString();
            entityDR.IsValidForAdvancedFind = entityMetadata.IsValidForAdvancedFind.Value.ToString();
            entityDR.LogicalName = entityMetadata.LogicalName.ToString();
            entityDR.MetadataId = entityMetadata.MetadataId.ToString();
            entityDR.ObjectTypeCode = entityMetadata.ObjectTypeCode.ToString();
            entityDR.OwnershipType = entityMetadata.OwnershipType.Value.ToString();
            entityDR.PrimaryIdAttribute = entityMetadata.PrimaryIdAttribute.ToString();
            if (entityMetadata.PrimaryNameAttribute != null)
                entityDR.PrimaryNameAttribute = entityMetadata.PrimaryNameAttribute.ToString();
            entityDR.SchemaName = entityMetadata.SchemaName.ToString();
            _entityDT.AddEntityRow(entityDR);
        }

        #endregion Private Methods
    }
}
